﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Practices.Unity;
using NHibernate;
using NHibernate.Linq;
using Web7.Domain;
using Web7.Ioc;
using Web7.Logging;

namespace Web7.Core.Domain.NHibernate
{
    /// <summary>
    /// NHibernate资源库基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class NHibernateRepository<T, TId> : NHibernateBase, IRepository<T, TId> where T : Entity<TId>
    {
        protected IDependencyInjector DependencyInjector;

        public NHibernateRepository(IDependencyInjector dependencyInjector, ISessionFactory sessionFactory)
            : base(sessionFactory)
        {
            DependencyInjector = dependencyInjector;
        }

        public NHibernateRepository(IDependencyInjector dependencyInjector)
            : this(dependencyInjector, dependencyInjector.GetService<ISessionFactory>())
        {
        }

        public virtual IQueryable<T> Table
        {
            get { return Session.Query<T>(); }
        }

        public virtual T Get(TId id)
        {
            return Session.Get<T>(id);
        }

        public virtual T Get(Expression<Func<T, bool>> predicate)
        {
            return Fetch(predicate).FirstOrDefault();
        }

        public virtual void Create(T entity)
        {
			LogHelper.Trace(this.GetType(), "Create {0}", () => entity);
            Transact(() => Session.SaveOrUpdate(entity));
        }

        public virtual void Update(T entity)
        {
			LogHelper.Trace(this.GetType(), "Update {0}", () => entity);
            Transact(() =>
            {
                Session.Evict(entity);
                Session.Merge(entity);
            });
        }

        public virtual void Delete(T entity)
        {
			LogHelper.Trace(this.GetType(), "Delete {0}", () => entity);
            Transact(() =>
            {
                Session.Delete(entity);
                Flush();
            });
        }

        public virtual void Delete(IEnumerable<T> listModels)
        {
            Transact(() =>
            {
                foreach (var entity in listModels)
                {
                    Delete(entity);
                }
            });
        }

        public void Delete(params TId[] ids)
        {
            if (ids != null && ids.Length > 0)
                Delete(m => ids.Contains(m.Id));
        }

        public virtual void Delete(Expression<Func<T, bool>> predicate)
        {
            Transact(() =>
            {
                IQueryable<T> queryable = Table.Where(predicate);
                if (queryable.Any())
                {
                    Delete(queryable);
                }
            });
        }

        public virtual void Copy(T source, T target)
        {
			LogHelper.Trace(this.GetType(), "Copy {0} {1}",
				() => source,
				() => target);

            var metadata = Session.SessionFactory.GetClassMetadata(typeof(T));
            var values = metadata.GetPropertyValues(source, EntityMode.Poco);
            metadata.SetPropertyValues(target, values, EntityMode.Poco);
        }

        public virtual void Flush()
        {
            Session.Flush();
        }

        public virtual int Count(Expression<Func<T, bool>> predicate)
        {
            return Fetch(predicate).Count();
        }

        public virtual IQueryable<T> Fetch(Expression<Func<T, bool>> predicate)
        {
            return Table.Where(predicate);
        }

        public virtual IQueryable<T> Fetch(Expression<Func<T, bool>> predicate, Action<Orderable<T>> order)
        {
            var orderable = new Orderable<T>(Fetch(predicate));
            order(orderable);
            return orderable.Queryable;
        }

        public virtual IQueryable<T> Fetch(Expression<Func<T, bool>> predicate, Action<Orderable<T>> order, int skip,
                                           int count)
        {
            return Fetch(predicate, order).Skip(skip).Take(count);
        }

        #region IRepository member

        void IRepository<T, TId>.Create(T entity)
        {
            Create(entity);
        }

        void IRepository<T, TId>.Update(T entity)
        {
            Update(entity);
        }

        void IRepository<T, TId>.Delete(T entity)
        {
            Delete(entity);
        }

        void IRepository<T, TId>.Delete(IEnumerable<T> listModels)
        {
            Delete(listModels);
        }

        void IRepository<T, TId>.Delete(params TId[] ids)
        {
            Delete(ids);
        }

        void IRepository<T, TId>.Delete(Expression<Func<T, bool>> predicate)
        {
            Delete(predicate);
        }

        void IRepository<T, TId>.Copy(T source, T target)
        {
            Copy(source, target);
        }

        void IRepository<T, TId>.Flush()
        {
            Flush();
        }

        T IRepository<T, TId>.Get(TId id)
        {
            return Get(id);
        }

        T IRepository<T, TId>.Get(Expression<Func<T, bool>> predicate)
        {
            return Get(predicate);
        }

        IQueryable<T> IRepository<T, TId>.Table
        {
            get { return Table; }
        }

        int IRepository<T, TId>.Count(Expression<Func<T, bool>> predicate)
        {
            return Count(predicate);
        }

        IEnumerable<T> IRepository<T, TId>.Fetch(Expression<Func<T, bool>> predicate)
        {
            return Fetch(predicate);
        }

        IEnumerable<T> IRepository<T, TId>.Fetch(Expression<Func<T, bool>> predicate, Action<Orderable<T>> order)
        {
            return Fetch(predicate, order);
        }

        IEnumerable<T> IRepository<T, TId>.Fetch(Expression<Func<T, bool>> predicate, Action<Orderable<T>> order, int skip, int count)
        {
            return Fetch(predicate, order, skip, count);
        }
        #endregion

    }

    public class NHibernateRepository<T> : NHibernateRepository<T, Guid>, IRepository<T> where T : Entity
    {
        public NHibernateRepository(IDependencyInjector dependencyInjector, ISessionFactory sessionFactory)
            : base(dependencyInjector, sessionFactory)
        {
        }

        public NHibernateRepository(IDependencyInjector dependencyInjector)
            : base(dependencyInjector)
        {
        }
    }
}
